import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import os
import tempfile
import subprocess
from PIL import Image
import img2pdf

# 导入Windows扫描相关模块（使用WIA - Windows Image Acquisition）
try:
    import win32com.client
    HAS_WIA = True
except ImportError:
    HAS_WIA = False

class ScanApp:
    def __init__(self, root):
        self.root = root
        self.root.title("快速扫描PDF")
        self.root.geometry("500x300")
        # self.root.resizable(False, False)
        self.root.configure(bg="#f0f0f0")

        # 设置ttk样式
        self.style = ttk.Style()
        self.style.theme_use('clam')  # 使用clam主题

        # 配置按钮样式
        self.style.configure('Scan.TButton',
                            font=('Arial', 14),
                            padding=15,
                            width=15,
                            borderwidth=0,
                            relief='flat')
        self.style.layout('Scan.TButton',
                         [('Button.padding', {'children': [('Button.label', {'sticky': 'nswe'})], 'sticky': 'nswe'})])
        self.style.map('Scan.TButton',
                      background=[('active', '#45a049'), ('!active', '#4CAF50')],
                      foreground=[('active', 'white'), ('!active', 'white')])

        # 配置标签样式
        self.style.configure('Status.TLabel',
                            font=('Arial', 10),
                            foreground='#333333',
                            background='#f0f0f0')

        # 配置标题标签样式
        self.style.configure('Title.TLabel',
                            font=('Arial', 12),
                            foreground='#333333',
                            background='#f0f0f0')

        # 配置主框架样式
        self.style.configure('Main.TFrame',
                            background='#f0f0f0')

        # 创建主框架
        self.main_frame = ttk.Frame(root, style='Main.TFrame')
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)

        # 配置按钮样式
        self.style.configure('Browse.TButton',
                            font=('Arial', 10),
                            padding=5)
        self.style.map('Browse.TButton',
                      background=[('active', '#e0e0e0'), ('!active', '#f0f0f0')])

        # 配置输入框样式
        self.style.configure('Path.TEntry',
                            padding=5)

        # 设置默认保存目录
        self.default_save_dir = os.path.join(os.path.expanduser("~"), "Documents")
        self.save_dir_var = tk.StringVar(value=self.default_save_dir)

        # 检查是否支持WIA
        if not HAS_WIA:
            messagebox.showerror("错误", "需要安装pywin32库来支持Windows扫描功能\n请运行: pip install pywin32")

        # 创建保存目录标签
        # dir_label = ttk.Label(self.main_frame, text="PDF保存目录:", style='Title.TLabel')
        # dir_label.pack(anchor=tk.W, pady=(10, 5))

        # 创建扫描仪选择框架
        scanner_frame = ttk.Frame(self.main_frame, style='Main.TFrame')
        scanner_frame.pack(fill=tk.X, pady=5)

        # 创建扫描仪标签
        scanner_label = ttk.Label(scanner_frame, text="选择扫描仪:", style='Title.TLabel')
        scanner_label.pack(side=tk.LEFT, padx=(0, 5))

        # 创建扫描仪下拉框
        self.scanner_var = tk.StringVar()
        self.scanner_combobox = ttk.Combobox(scanner_frame, textvariable=self.scanner_var, state="readonly", width=30)
        self.scanner_combobox.pack(side=tk.LEFT, fill=tk.X, expand=True)

        # 创建目录输入框和浏览按钮的框架
        path_frame = ttk.Frame(self.main_frame, style='Main.TFrame')
        path_frame.pack(fill=tk.X, pady=5)

        # 创建目录输入框
        self.dir_entry = ttk.Entry(path_frame, textvariable=self.save_dir_var, style='Path.TEntry', width=40)
        self.dir_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))

        # 创建浏览按钮
        self.browse_button = ttk.Button(path_frame, text="选择", command=self.browse_directory, style='Browse.TButton',width=5)
        self.browse_button.pack(side=tk.LEFT, padx=(0, 5))

        # 创建打开目录按钮
        self.open_dir_button = ttk.Button(path_frame, text="打开", command=self.open_directory, style='Browse.TButton',width=5)
        self.open_dir_button.pack(side=tk.LEFT)

        # 创建文件名前缀和序号输入框框架
        filename_frame = ttk.Frame(self.main_frame, style='Main.TFrame')
        filename_frame.pack(fill=tk.X, pady=5)

        # 文件名前缀标签和输入框
        prefix_label = ttk.Label(filename_frame, text="文件:", style='Title.TLabel')
        prefix_label.pack(side=tk.LEFT, padx=(0, 5))
        self.prefix_var = tk.StringVar(value="扫描件")
        self.prefix_entry = ttk.Entry(filename_frame, textvariable=self.prefix_var, style='Path.TEntry', width=15)
        self.prefix_entry.pack(side=tk.LEFT, padx=(0, 20))

        # 序号标签和输入框
        counter_label = ttk.Label(filename_frame, text="-", style='Title.TLabel')
        counter_label.pack(side=tk.LEFT)
        self.counter_var = tk.IntVar(value=1)
        self.counter_entry = ttk.Entry(filename_frame, textvariable=self.counter_var, style='Path.TEntry', width=5)
        self.counter_entry.pack(side=tk.LEFT)

        # 创建扫描按钮
        self.scan_button = ttk.Button(
            self.main_frame,
            text="扫描文档",
            command=self.scan_document,
            style='Scan.TButton'
        )
        self.scan_button.pack(pady=20)

        # 添加状态标签
        self.status_var = tk.StringVar()
        self.status_var.set("准备就绪")
        self.status_label = ttk.Label(self.main_frame, textvariable=self.status_var, style='Status.TLabel')
        self.status_label.pack(pady=10)

        # 获取并显示可用扫描仪
        self._init_scanner_combobox()

    def browse_directory(self):
        """浏览选择保存目录"""
        directory = filedialog.askdirectory(title="选择文件保存目录")
        if directory:
            self.save_dir_var.set(directory)

    def open_directory(self):
        """打开保存目录"""
        directory = self.save_dir_var.get()
        if os.path.isdir(directory):
            try:
                # 确保路径正确编码
                directory = os.path.normpath(directory)
                # 使用os.startfile打开目录，更好地处理中文路径
                os.startfile(directory)
            except FileNotFoundError:
                messagebox.showerror("错误", "目录不存在或无法访问")
            except Exception as e:
                messagebox.showerror("错误", f"打开目录失败:\n{str(e)}")
        else:
            messagebox.showerror("错误", "指定的目录不存在")

    def scan_document(self):
        """扫描文档并保存为PDF"""
        if not HAS_WIA:
            messagebox.showerror("错误", "缺少WIA支持，请安装pywin32")
            return

        try:
            # 验证保存目录是否存在
            save_dir = self.save_dir_var.get()
            if not os.path.isdir(save_dir):
                messagebox.showerror("错误", "保存目录不存在，请先选择有效的目录")
                return

            self.status_var.set("正在连接扫描仪...")
            self.root.update()

            # 使用WIA扫描
            images = self._scan_with_wia()

            if not images:
                messagebox.showinfo("信息", "未扫描到任何图像")
                self.status_var.set("准备就绪")
                return

            # 保存为PDF
            self.status_var.set("正在保存为PDF...")
            self.root.update()

            # 默认文件名
            pdf_name = os.path.join(save_dir, f"{self.prefix_var.get()}-{self.counter_var.get():03d}.pdf")

            self._save_images_to_pdf(images, pdf_name)
            self.status_var.set("成功扫描文件：" + pdf_name)
            # 更新序号
            self.counter_var.set(self.counter_var.get() + 1)

            # save_path = filedialog.asksaveasfilename(
            #     defaultextension=".pdf",
            #     filetypes=[("PDF文件", "*.pdf")],
            #     title="保存PDF文件",
            #     initialdir=save_dir,
            #     initialfile=f"{self.prefix_var.get()}-{self.counter_var.get():03d}"
            # )

            # if save_path:
            #     self._save_images_to_pdf(images, save_path)
            #     messagebox.showinfo("成功", f"PDF文件已保存到:\n{save_path}")
            #     self.status_var.set("准备就绪")
            #     # 更新序号
            #     self.counter_var.set(self.counter_var.get() + 1)
            # else:
            #     self.status_var.set("保存已取消")

        except Exception as e:
            messagebox.showerror("错误", f"扫描过程中出现错误:\n{str(e)}")
            self.status_var.set("准备就绪")
        finally:
            # 清理临时文件
            if 'images' in locals():
                for img_path in images:
                    try:
                        if os.path.exists(img_path):
                            os.remove(img_path)
                    except:
                        pass

    def _get_scanner_devices(self):
        """获取可用的扫描仪设备列表"""
        devices = []
        try:
            wia_dev_manager = win32com.client.Dispatch("WIA.DeviceManager")
            for device_info in wia_dev_manager.DeviceInfos:
                if device_info.Type == 1:  # 1 表示扫描仪设备
                    devices.append(device_info)
            return devices
        except Exception as e:
            print(f"获取扫描仪设备失败: {e}")
            return []

    def _init_scanner_combobox(self):
        """初始化扫描仪下拉选择框"""
        scanners = self._get_scanner_devices()
        if scanners:
            self.scanner_combobox['values'] = [scanner.Properties("Name").Value for scanner in scanners]
            self.scanner_combobox.current(0)  # 默认选择第一个扫描仪
        else:
            messagebox.showwarning("警告", "未检测到可用的扫描仪设备")
            self.scanner_combobox['values'] = ["无可用扫描仪"]
            self.scanner_combobox.current(0)
            self.scan_button.config(state=tk.DISABLED)

    def _scan_with_wia(self):
        """使用Windows Image Acquisition扫描图像"""
        scanners = self._get_scanner_devices()
        selected_index = self.scanner_combobox.current()

        if selected_index < 0 or not scanners:
            raise Exception("未选择有效的扫描仪")

        try:
            # 连接到选中的扫描仪
            device = scanners[selected_index].Connect()
            wia = win32com.client.Dispatch("WIA.CommonDialog")

            # 获取第一个扫描项
            item = device.Items[0]

            # 设置扫描参数以优化文件大小
            for prop in item.Properties:
                if prop.Name == "Horizontal Resolution":
                    prop.Value = 200  # 降低分辨率至200dpi，平衡清晰度和文件大小，1页200 k 左右，300 700k 左右
                elif prop.Name == "Vertical Resolution":
                    prop.Value = 200
                elif prop.Name == "Color Mode":
                    prop.Value = 2  # 2： 彩色，0 黑白，1 = 灰度模式，比彩色模式文件小60-70%
                # elif prop.Name == "Brightness":
                #     prop.Value = 50  # 亮度适中（0-100）
                # elif prop.Name == "Contrast":
                #     prop.Value = 0  # 对比度适中（0-100）
                # 可根据需要添加其他参数：
                # elif prop.Name == "Horizontal Extent":  # 扫描宽度
                #     prop.Value = 8.5  # 单位通常为英寸
                # elif prop.Name == "Vertical Extent":    # 扫描高度
                #     prop.Value = 11   # 单位通常为英寸

            # 传输图像
            image = item.Transfer()

            # 保存扫描的图像到临时文件
            temp_filename = os.path.join(tempfile.gettempdir(), f"scan_{id(image)}.jpg")
            image.SaveFile(temp_filename)
            return [temp_filename]

        except Exception as e:
            # 尝试更通用的方法
            try:
                # 创建图像文件
                image_file = wia.ShowAcquireImage()
                if not image_file:
                    return []

                # 保存临时图像
                temp_dir = tempfile.gettempdir()
                temp_image_path = os.path.join(temp_dir, f"scan_temp_{os.getpid()}.jpg")
                image_file.SaveFile(temp_image_path)

                return [temp_image_path]
            except:
                # 如果两种方法都失败，抛出原始异常
                raise e

    def _save_images_to_pdf(self, image_paths, pdf_path):
        """将图像列表保存为PDF文件"""
        # 确保所有图像都是RGB模式
        processed_images = []
        for img_path in image_paths:
            with Image.open(img_path) as img:
                if img.mode != "RGB":
                    img = img.convert("RGB")
                temp_rgb_path = img_path + ".rgb.jpg"
                img.save(temp_rgb_path, "JPEG")
                processed_images.append(temp_rgb_path)

        # 使用img2pdf创建PDF
        with open(pdf_path, "wb") as f:
            f.write(img2pdf.convert(processed_images))

        # 清理处理后的临时图像
        for img_path in processed_images:
            try:
                if os.path.exists(img_path):
                    os.remove(img_path)
            except:
                pass

if __name__ == "__main__":
    root = tk.Tk()
    app = ScanApp(root)
    root.mainloop()